<?php
/**
 * 磁盘缓存类
 * 说明：
 * 
 * @date 2010年9月17日16:18:07
 * @author Wang Penghai <Penghai@leju.sina.com.cn>
 * @version : $Id$
 */
class pf_cache {

	// 类成员 begin
	/*
	 * 保存路径
	 * @var string
	 */
	protected $path = './';
	/**
	 * 文件前缀
	 * @var string
	 */
	protected $prefix = 'pf_';
	/**
	 * 是否有子目录-- 默认是 true
	 * @var boolean
	 */
	protected $is_subdir = true;
	/**
	 * 目录级数 -- 默认是3级
	 * @var int
	 */
	protected $path_level = 3;
	/**
	 * 有效时间 0 为永久有效
	 * @var int
	 */
	protected $expire = 0;
	/**
	 * 是否启动压缩
	 * @var Boolean
	 */
	protected $is_compress = false;
	/**
	 * 是否启动数据校验
	 * @var boolean
	 */
	protected $is_cache_check = false;
	/**
	 * 目录权限
	 * @var int
	 */
	protected $dir_mode = "0777";
	// 类成员 end

	/**
	 * 构造函数
	 *
	 * @param array $param 参数数组， 参考 类成员
	 *
	 * @return void
	 */
	public function __construct(array $param=array())
	{
		 $this->set_param($param);
	}
	/**
	 * 设置参数
	 * @param array $param 参数数组， 参考 类成员
	 *
	 * @return void
	 */
	public function set_param(array $param = array())
	{
		if (isset($param['path']))
		{
			if (is_dir($param['path']))
			{
				$this->path = $param['path'];
			}
		}
		if (isset($param['prefix']))
		{
			$this->prefix = $param['prefix'];
		}
		if (isset($param['path_level']))
		{
			$this->path_level = (int)$param['path_level'];
		}
		if (isset($param['expire']))
		{
			$this->expire = (int)$param['expire'];
		}
		if (isset($param['dir_mode']))
		{
			$this->dir_mode = $param['dir_mode'];
		}
		if (isset($param['is_subdir']))
		{
			$this->is_subdir = (boolean)$param['is_subdir'];
		}
		if (isset($param['is_compress']))
		{
			$this->is_compress = (boolean)$param['is_compress'];
		}
		if (isset($param['is_cache_check']))
		{
			$this->is_cache_check = (boolean)$param['is_cache_check'];
		}

	}
	
	/**
	 * 写缓存
	 *
	 * @param string $filename 文件名
	 * @param array | string $data 数据, 可以是数组（数组保存时执行 serialize）， 也可是字符串
	 * @param Boolean $is_over 是否强制覆盖， 默认是否。
	 *
	 * @return 成功 true | 失败 错误信息数组 例如： array('errorno'=>1, 'msg'=>'该文件存在，强制覆盖请设$is_over=true')
	 * @see self::file_name();
	 */
	public function write($filename, $data, $is_over = false)
	{
		$file_path = $this->hash_path($filename);
		if(!is_dir($file_path))
		{
			if (false === self::make_dir($file_path, $this->dir_mode))
			{
				return false;
			}
		}

		$filename = $file_path . DIRECTORY_SEPARATOR . $this->get_cache_name($filename);
		$data = serialize($data);
		if( $this->is_compress && function_exists('gzcompress'))
		{
			//数据压缩
			$data = gzcompress($data, 3);
		}

		if($this->is_cache_check) 
		{
			//开启数据校验
			$check  =  md5($data);
		}else {
			$check  =  '';
		}
		$data	= "<?php\n//" . sprintf('%012d', $this->expire) . $check . $data . "\n?>";

		$result  =   file_put_contents($filename, $data);
		if($result)
		{
			clearstatcache();
			return true;
		}else {
			return false;
		}
	}
	
	/**
	 * 读取缓存文件
	 * 说明：
	 * 如果缓存文件是被serialize 了， 返回结果会 unserialize 
	 *
	 * @param string $filename 文件名
	 *
	 * @return 成功 数据（字符串或数组） | 失败 false 保存错误信息
	 * @see self::hash_path();
	 */
	public function read($filename)
	{
		$file_path  = $this->hash_path($filename);
		$filename = $file_path . DIRECTORY_SEPARATOR . $this->get_cache_name($filename);
		if (!is_file($filename))
		{
		   return false;
		}

		$content	=   file_get_contents($filename);
		if( false !== $content)
		{
			$expire  =  (int)substr($content,8, 12);
			if($expire != 0 && time() > filemtime($filename) + $expire) 
			{
				//缓存过期删除缓存文件
				$this->delete($filename);
				return false;
			}

			if($this->is_cache_check) 
			{//开启数据校验
				$check  =  substr($content,20, 32);
				$content   =  substr($content,52, -3);
				if($check != md5($content))
				{
					//校验错误
					return false;
				}
			}
			else
			{
				$content = substr($content,20, -3);
			}
			if($this->is_compress && function_exists('gzcompress'))
			{
				//启用数据压缩
				$content = gzuncompress($content);
			}

			$content = unserialize($content);
			return $content;
		}
		else
		{
			return false;
		}
	}

	/**
	 * 删除缓存
	 *
	 * @param string $filename 文件名
	 *
	 * @return 成功 TRUE | 失败 FALSE
	 */
	public function delete($filename)
	{
		if (unlink($filename))
		{
			$this->rm_empty_dir(dirname($filename));
			return true;
		}
		return false;
	}

	/**
	 * 删除目录
	 *
	 * @param string $dirname 目录名
	 *
	 * @return 成功 TRUE | 失败 FALSE
	 * @see self::file_name();
	 */
	public function rm_dir($dirname)
	{
		$res = rmdir($dirname);
		return $res;
	}

	/**
	 * 建目录
	 *
	 * @param string $dir 目录字符串
	 * @param int $mode 权限数字
	 *
	 * @return 成功 true | 失败 false
	 */
	public static function make_dir($dir, $mode = "0777")
	{
		if(!is_dir($dir))
		{
			if(!self::make_dir(dirname($dir), $mode))
			{
				return false;
			}
			if(!mkdir($dir))
			{
				return false;
			}
		}
		return true;
	}

	/**
	 * 删除空目录
	 *
	 * @param string $path 目录字符串
	 *
	 * @return 成功 true | 失败 false
	 */
	public function rm_empty_dir($path)
	{
		$path = realpath($path);
		$this->path = realpath($this->path);
		$is_empty_dir = true;//是否是空目录 -- 默认是true
		if ($dir = opendir($path))
		{
			while(false !== ($file = readdir($dir)))
			{
				if ($file !== '.' && $file !== '..')
				{
					$is_empty_dir = false;
					break;
				}
			}
			closedir($dir);
			if ($is_empty_dir)
			{
				chdir($path);
				chdir('..');
				$this->rm_dir($path);
				//删除上级目录
				$cur_path = getcwd();
				$this->path;
				if ($this->path != $cur_path)
				{
					self::rm_empty_dir($cur_path);
				}
			}
			
		}
	}

	/**
	 * 生成保存文件子路径
	 *
	 * @return string $path 缓存路径
	 */
	protected function hash_path($name)
	{
		$name = md5($name);
		if($this->is_subdir) 
		{
			// 使用子目录
			$dir = '';
			for($i=0; $i < $this->path_level; $i++)
			{
				$dir .= $name{$i} . DIRECTORY_SEPARATOR;
			}
			return $this->path . $dir;
		}else{
			return $this->path;
		}
	}

	/**
	 * 生成缓存文件名
	 *
	 * @param $name 原文件名
	 *
	 * @return string $cache_name 缓存文件名
	 */
	 protected function get_cache_name($name)
	{
		$cache_name = $this->prefix . md5($name) . '.php';
		return $cache_name;
	}

}

?>  